home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectInput / DIConfig / cdiacpage.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  50.3 KB  |  1,853 lines

  1. //-----------------------------------------------------------------------------
  2. // File: cdiacpage.cpp
  3. //
  4. // Desc: CDIDeviceActionConfigPage implements the page object used by the UI.
  5. //       A page covers the entire UI minus the device tabs and the bottons at
  6. //       the bottom.  The information window, player combo-box, genre combo-
  7. //       box, action list tree, and device view window are all managed by
  8. //       the page.
  9. //
  10. // Copyright (C) 1999-2001 Microsoft Corporation. All Rights Reserved.
  11. //-----------------------------------------------------------------------------
  12.  
  13. #include "common.hpp"
  14. #include <initguid.h>
  15.  
  16. DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
  17.  
  18.  
  19. // {D0B5C9AE-966F-4510-B955-4D2482C5EB1B}
  20. DEFINE_GUID(GUID_ActionItem, 
  21. 0xd0b5c9ae, 0x966f, 0x4510, 0xb9, 0x55, 0x4d, 0x24, 0x82, 0xc5, 0xeb, 0x1b);
  22.  
  23.  
  24. #define DISEM_TYPE_MASK                    ( 0x00000600 )
  25. #define DISEM_REL_MASK                     ( 0x00000100 )
  26. #define DISEM_REL_SHIFT                    ( 8 ) 
  27. #define DISEM_TYPE_AXIS                    0x00000200
  28. #define DISEM_TYPE_BUTTON                  0x00000400
  29. #define DISEM_TYPE_POV                     0x00000600
  30.  
  31. #define DEVICE_POLLING_INTERVAL 10
  32. #define DEVICE_POLLING_AXIS_MIN 0
  33. #define DEVICE_POLLING_AXIS_MAX 100
  34. #define DEVICE_POLLING_AXIS_MINDELTA 3
  35. #define DEVICE_POLLING_AXIS_SIGNIFICANT 40
  36. #define DEVICE_POLLING_AXIS_ACCUMULATION 20
  37. #define DEVICE_POLLING_ACBUF_START_INDEX 3
  38. #define DEVICE_POLLING_WHEEL_SCALE_FACTOR 3
  39.  
  40. // For WINMM.DLL
  41. HINSTANCE g_hWinMmDLL = NULL;
  42. FUNCTYPE_timeSetEvent g_fptimeSetEvent = NULL;
  43.  
  44. //QueryInterface
  45. STDMETHODIMP CDIDeviceActionConfigPage::QueryInterface(REFIID iid, LPVOID* ppv)
  46. {
  47.    //null the out param
  48.     *ppv = NULL;
  49.  
  50.     if ((iid == IID_IUnknown) || (iid == IID_IDIDeviceActionConfigPage))
  51.     {
  52.         *ppv = this;
  53.         AddRef();
  54.         return S_OK;
  55.     }
  56.  
  57.     return E_NOINTERFACE;
  58. }
  59.  
  60.  
  61. //AddRef
  62. STDMETHODIMP_(ULONG) CDIDeviceActionConfigPage::AddRef()
  63. {
  64.     return InterlockedIncrement(&m_cRef);
  65. }
  66.  
  67.  
  68. //Release
  69. STDMETHODIMP_(ULONG) CDIDeviceActionConfigPage::Release()
  70. {
  71.  
  72.     if (InterlockedDecrement(&m_cRef) == 0)
  73.     {
  74.         delete this;
  75.         return 0;
  76.     }
  77.  
  78.     return m_cRef;
  79. }
  80.  
  81.  
  82. //constructor
  83. CDIDeviceActionConfigPage::CDIDeviceActionConfigPage() :
  84.     m_pDeviceUI(NULL), m_puig(NULL), m_pUIFrame(NULL),
  85.     m_cRef(1), m_lpDiac(NULL), m_State(CFGSTATE_NORMAL),
  86.     m_pCurControl(NULL),
  87.     m_tszIBText(NULL), m_pbmIB(NULL), m_pbmIB2(NULL),
  88.     m_pbmRelAxesGlyph(NULL), m_pbmAbsAxesGlyph(NULL), m_pbmButtonGlyph(NULL),
  89.     m_pbmHatGlyph(NULL), m_pbmCheckGlyph(NULL), m_pbmCheckGlyphDark(NULL),
  90.     m_pRelAxesParent(NULL), m_pAbsAxesParent(NULL), m_pButtonParent(NULL),
  91.     m_pHatParent(NULL),    m_pUnknownParent(NULL),
  92.     m_bFirstDeviceData(TRUE), m_cbDeviceDataSize(0), m_nOnDeviceData(0),
  93.     m_dwLastControlType(0),
  94.     m_nPageIndex(-1)
  95. {
  96.     tracescope(__ts, _T("CDIDeviceActionConfigPage::CDIDeviceActionConfigPage()\n"));
  97.     m_pDeviceData[0] = NULL;
  98.     m_pDeviceData[1] = NULL;
  99. }
  100.  
  101.  
  102. //destructor
  103. CDIDeviceActionConfigPage::~CDIDeviceActionConfigPage()
  104. {
  105.     tracescope(__ts, _T("CDIDeviceActionConfigPage::~CDIDeviceActionConfigPage()\n"));
  106.  
  107.     // Unattach the parent from the tooltip window so it won't get destroyed.
  108.     SetParent(CFlexWnd::s_ToolTip.m_hWnd, NULL);
  109.  
  110.     if (m_hWnd != NULL)
  111.         Destroy();
  112.  
  113.     FreeResources();
  114.  
  115.     delete m_pDeviceUI;
  116.  
  117.     for (int c = 0; c < 2; c++)
  118.         if (m_pDeviceData[c] != NULL)
  119.             free(m_pDeviceData[c]);
  120.  
  121.     if (m_lpDID != NULL)
  122.     {
  123.         m_lpDID->Unacquire();
  124.         m_lpDID->Release();
  125.     }
  126.     m_lpDID = NULL;
  127. }
  128.  
  129.  
  130. STDMETHODIMP CDIDeviceActionConfigPage::Create(DICFGPAGECREATESTRUCT *pcs)
  131. {
  132.     tracescope(__ts, _T("CDIDeviceActionConfigPage::Create()\n"));
  133.     if (pcs == NULL)
  134.         return E_INVALIDARG;
  135.     DICFGPAGECREATESTRUCT &cs = *pcs;
  136.     
  137.     // validate/save uig and uif
  138.     m_puig = pcs->pUIGlobals;
  139.     m_pUIFrame = pcs->pUIFrame;
  140.     if (m_puig == NULL || m_pUIFrame == NULL)
  141.         return E_INVALIDARG;
  142.  
  143.     // save page index
  144.     m_nPageIndex = pcs->nPage;
  145.     assert(m_nPageIndex >= 0);
  146.  
  147.     // create deviceui with uig, or fail
  148.     m_pDeviceUI = new CDeviceUI(*m_puig, *m_pUIFrame);
  149.     if (m_pDeviceUI == NULL)
  150.         return E_FAIL;
  151.  
  152.     // save the device instance
  153.     m_didi = cs.didi;
  154.     m_lpDID = cs.lpDID;
  155.     if (m_lpDID != NULL)
  156.         m_lpDID->AddRef();
  157.  
  158.     // create the window
  159.     HWND hWnd = NULL;
  160.     assert(m_puig != NULL);
  161.     RECT rect = {0, 0, 1, 1};
  162.     hWnd = CFlexWnd::Create(cs.hParentWnd, rect, FALSE);
  163.  
  164.     // return the handle
  165.     cs.hPageWnd = hWnd;
  166.  
  167.     assert(m_puig != NULL);
  168.  
  169.     // Create the information box
  170.     m_InfoBox.Create(m_hWnd, g_InfoWndRect, TRUE);
  171.     m_InfoBox.SetFont((HFONT)m_puig->GetFont(UIE_USERNAMES));
  172.     m_InfoBox.SetColors(m_puig->GetTextColor(UIE_USERNAMES),
  173.                         m_puig->GetBkColor(UIE_USERNAMES),
  174.                         m_puig->GetTextColor(UIE_USERNAMESEL),
  175.                         m_puig->GetBkColor(UIE_USERNAMESEL),
  176.                         m_puig->GetBrushColor(UIE_USERNAMES),
  177.                         m_puig->GetPenColor(UIE_USERNAMES));
  178.     SetAppropriateDefaultText();
  179.  
  180.     // Create the check box only if this is a keyboard device.
  181.     if (LOBYTE(LOWORD(m_didi.dwDevType)) == DI8DEVTYPE_KEYBOARD)
  182.     {
  183.         m_CheckBox.Create(m_hWnd, g_CheckBoxRect, FALSE);
  184.         m_CheckBox.SetNotify(m_hWnd);
  185.         m_CheckBox.SetFont((HFONT)m_puig->GetFont(UIE_USERNAMES));
  186.         m_CheckBox.SetColors(m_puig->GetTextColor(UIE_USERNAMES),
  187.                              m_puig->GetBkColor(UIE_USERNAMES),
  188.                              m_puig->GetTextColor(UIE_USERNAMESEL),
  189.                              m_puig->GetBkColor(UIE_USERNAMESEL),
  190.                              m_puig->GetBrushColor(UIE_USERNAMES),
  191.                              m_puig->GetPenColor(UIE_USERNAMES));
  192.  
  193.         TCHAR tszResourceString[MAX_PATH];
  194.         LoadString(g_hModule, IDS_SORTASSIGNED, tszResourceString, MAX_PATH);
  195.         m_CheckBox.SetText(tszResourceString);
  196.         m_CheckBox.SetCheck(TRUE);
  197.         ::ShowWindow(m_CheckBox.m_hWnd, SW_SHOW);
  198.     }
  199.  
  200.     // create the username dropdown if necessary
  201.     FLEXCOMBOBOXCREATESTRUCT cbcs;
  202.     cbcs.dwSize = sizeof(FLEXCOMBOBOXCREATESTRUCT);
  203.     cbcs.dwFlags = FCBF_DEFAULT;
  204.     cbcs.dwListBoxFlags = FCBF_DEFAULT|FLBF_INTEGRALHEIGHT;
  205.     cbcs.hWndParent = m_hWnd;
  206.     cbcs.hWndNotify = m_hWnd;
  207.     cbcs.bVisible = TRUE;
  208.     cbcs.rect = g_UserNamesRect;
  209.     cbcs.hFont = (HFONT)m_puig->GetFont(UIE_USERNAMES);
  210.     cbcs.rgbText = m_puig->GetTextColor(UIE_USERNAMES);
  211.     cbcs.rgbBk = m_puig->GetBkColor(UIE_USERNAMES);
  212.     cbcs.rgbSelText = m_puig->GetTextColor(UIE_USERNAMESEL);
  213.     cbcs.rgbSelBk = m_puig->GetBkColor(UIE_USERNAMESEL);
  214.     cbcs.rgbFill = m_puig->GetBrushColor(UIE_USERNAMES);
  215.     cbcs.rgbLine = m_puig->GetPenColor(UIE_USERNAMES);
  216.     cbcs.nSBWidth = 11;
  217.  
  218.     if (m_puig->GetNumUserNames() > 0 && m_hWnd != NULL
  219.        )
  220.     {
  221.         for (int i = 0, n = m_puig->GetNumUserNames(); i < n; i++)
  222.             m_UserNames.AddString(SAFESTR(m_puig->GetUserName(i)));
  223.         m_UserNames.AddString(SAFESTR(_T("(unassigned)")));
  224.  
  225.         m_UserNames.Create(&cbcs);
  226.  
  227.         int nUser = m_pUIFrame->GetCurUser(m_nPageIndex);
  228.         if (nUser == -1)
  229.             nUser = m_puig->GetNumUserNames();
  230.         m_UserNames.SetSel(nUser);
  231.     } else
  232.     if (m_hWnd != NULL)
  233.         m_UserNames.SetSel(0);  // If only 1 user, still must set selection to 0 or we get error later.
  234.  
  235.     // If we are in view mode, set username combobox to read only so user can't change its value.
  236.     if (!m_puig->InEditMode())
  237.         m_UserNames.SetReadOnly(TRUE);
  238.  
  239.     if (m_puig->GetNumMasterAcFors() > 1 && m_hWnd != NULL)
  240.     {
  241.         for (int i = 0, n = m_puig->GetNumMasterAcFors(); i < n; i++)
  242.             m_Genres.AddString(SAFESTR(m_puig->RefMasterAcFor(i).tszActionMap));
  243.  
  244.         cbcs.rect = g_GenresRect;
  245.         m_Genres.Create(&cbcs);
  246.         m_Genres.SetSel(m_pUIFrame->GetCurGenre());
  247.     }
  248.  
  249.     // return success/fail
  250.     return hWnd != NULL ? S_OK : E_FAIL;
  251. }
  252.  
  253. STDMETHODIMP CDIDeviceActionConfigPage::Show(LPDIACTIONFORMATW lpDiActFor)
  254. {
  255.     // save the format pointer
  256.     m_lpDiac = lpDiActFor;
  257.  
  258.     // force tree init
  259.     InitTree(TRUE);
  260.  
  261.     // show the assignments for the controls
  262.     SetControlAssignments();
  263.  
  264.     // show the assignment for the current control
  265.     ShowCurrentControlAssignment();
  266.  
  267.     // Sort the list if check box is checked.
  268.     if (m_CheckBox.GetCheck())
  269.         m_pDeviceUI->GetCurView()->SortAssigned(TRUE);
  270.  
  271.     // show the window
  272.     if (m_hWnd != NULL)
  273.         ShowWindow(m_hWnd, SW_SHOW);
  274.  
  275.     SetFocus(m_hWnd);
  276.     CFlexWnd::s_CurrPageHwnd = m_hWnd;
  277.  
  278.     return S_OK;
  279. }
  280.  
  281. STDMETHODIMP CDIDeviceActionConfigPage::Hide()
  282. {
  283.     // clear the tree
  284.     ClearTree();
  285.  
  286.     // null the format pointer
  287.     m_lpDiac = NULL;
  288.  
  289.     // hide the window
  290.     if (m_hWnd != NULL)
  291.         ShowWindow(m_hWnd, SW_HIDE);
  292.  
  293.     // If we are in the assign state, exit it.
  294.     if (m_State == CFGSTATE_ASSIGN)
  295.         ExitAssignState();
  296.  
  297.     return S_OK;
  298. }
  299.  
  300. void CDIDeviceActionConfigPage::InitIB()
  301. {
  302.     RECT z = {0,0,0,0};
  303.     SIZE bsize = {0,0};
  304.     m_rectIB = z;
  305.     if (m_pbmIB != NULL)
  306.     {
  307.         if (m_pbmIB->GetSize(&bsize))
  308.         {
  309.             m_rectIB.right = bsize.cx * 2;
  310.             m_rectIB.bottom = bsize.cy;
  311.         }
  312.     }
  313.  
  314.     const int IBORIGINX = 200, IBORIGINY = 394,
  315.         IBTEXTMARGINLEFT = 5;
  316.     POINT ptIBOrigin = {IBORIGINX, IBORIGINY};
  317.  
  318.     m_tszIBText = _T("Click here to see different views of your controller.");
  319.     SIZE tsize = GetTextSize(m_tszIBText, (HFONT)m_puig->GetFont(UIE_VIEWSEL));
  320.     m_ptIBOffset.x = 0;
  321.     m_ptIBOffset.y = 0;
  322.     int tofs = 0;
  323.     if (m_rectIB.bottom < tsize.cy)
  324.     {
  325.         m_rectIB.bottom = tsize.cy;
  326.         m_ptIBOffset.y = (tsize.cy - bsize.cy) / 2;
  327.     }
  328.     else if (tsize.cy < m_rectIB.bottom)
  329.         tofs = (bsize.cy - tsize.cy) / 2;
  330.     m_rectIB.right += tsize.cx;
  331.     if (m_pbmIB != NULL)
  332.         m_rectIB.right += IBTEXTMARGINLEFT * 2;
  333.  
  334.     OffsetRect(&m_rectIB, ptIBOrigin.x, ptIBOrigin.y);
  335.     m_ptIBOffset.x += ptIBOrigin.x;
  336.     m_ptIBOffset.y += ptIBOrigin.y;
  337.  
  338.     m_ptIBOffset2.x = m_rectIB.right - bsize.cx;
  339.     m_ptIBOffset2.y = m_ptIBOffset.y;
  340.  
  341.     m_rectIBText = m_rectIB;
  342.     if (m_pbmIB != NULL)
  343.         m_rectIBText.left += IBTEXTMARGINLEFT + bsize.cx;
  344.     if (m_pbmIB2 != NULL)
  345.         m_rectIBText.right -= IBTEXTMARGINLEFT + bsize.cx;
  346.     m_rectIBText.top += tofs;
  347.  
  348.     // Inialize the two RECTs representing the two arrow bitmaps
  349.     m_rectIBLeft = m_rectIBRight = m_rectIB;
  350.     m_rectIBLeft.right = m_rectIBText.left;
  351.     m_rectIBRight.left = m_rectIBText.right;
  352. }
  353.  
  354. void CDIDeviceActionConfigPage::OnInit()
  355. {
  356.     tracescope(__ts, _T("CDIDeviceActionConfigPage::OnInit()\n"));
  357.     // init resources
  358.     InitResources();
  359.  
  360.     // init IB
  361.     InitIB();
  362.  
  363.     // initialize the device UI
  364.     m_pDeviceUI->Init(m_didi, m_lpDID, m_hWnd, this);
  365.  
  366.     // initialize the device
  367.     InitDevice();
  368.  
  369.     // Start a one-shot timer for click to pick
  370.     if (g_fptimeSetEvent)
  371.         g_fptimeSetEvent(DEVICE_POLLING_INTERVAL, DEVICE_POLLING_INTERVAL,
  372.                          CDIDeviceActionConfigPage::DeviceTimerProc, (DWORD_PTR)m_hWnd, TIME_ONESHOT);
  373.  
  374.     // create the tree
  375.     CAPTIONLOOK cl;
  376.     cl.dwMask = CLMF_TEXTCOLOR | CLMF_FONT | CLMF_LINECOLOR;
  377.     cl.rgbTextColor = m_puig->GetTextColor(UIE_CALLOUT);
  378.     cl.rgbLineColor = m_puig->GetPenColor(UIE_BORDER);
  379.     cl.hFont = (HFONT)m_puig->GetFont(UIE_ACTION);
  380.     m_Tree.SetDefCaptionLook(cl);
  381.     cl.rgbTextColor = m_puig->GetTextColor(UIE_CALLOUTHIGH);
  382.     m_Tree.SetDefCaptionLook(cl, TRUE);
  383.     m_Tree.SetBkColor(RGB(0,0,0));
  384.     if (m_puig->InEditMode())
  385.     {
  386.         m_Tree.Create(m_hWnd, g_TreeRect, TRUE, TRUE);
  387.         m_Tree.SetScrollBarColors(
  388.             m_puig->GetBrushColor(UIE_SBTRACK),
  389.             m_puig->GetBrushColor(UIE_SBTHUMB),
  390.             m_puig->GetPenColor(UIE_SBBUTTON));
  391.     }
  392. }
  393.  
  394. void CDIDeviceActionConfigPage::InitResources()
  395. {
  396.     // create glyphs
  397.     if (!m_pbmRelAxesGlyph)
  398.         m_pbmRelAxesGlyph = CBitmap::CreateFromResource(g_hModule, IDB_AXESGLYPH);
  399.     if (!m_pbmAbsAxesGlyph)
  400.         m_pbmAbsAxesGlyph = CBitmap::CreateFromResource(g_hModule, IDB_AXESGLYPH);
  401.     if (!m_pbmButtonGlyph)
  402.         m_pbmButtonGlyph = CBitmap::CreateFromResource(g_hModule, IDB_BUTTONGLYPH);
  403.     if (!m_pbmHatGlyph)
  404.         m_pbmHatGlyph = CBitmap::CreateFromResource(g_hModule, IDB_HATGLYPH);
  405.     if (!m_pbmCheckGlyph)
  406.         m_pbmCheckGlyph = CBitmap::CreateFromResource(g_hModule, IDB_CHECKGLYPH);
  407.     if (!m_pbmCheckGlyphDark)
  408.         m_pbmCheckGlyphDark = CBitmap::CreateFromResource(g_hModule, IDB_CHECKGLYPHDARK);
  409.  
  410.     // create IB bitmaps
  411.     if (!m_pbmIB)
  412.         m_pbmIB = CBitmap::CreateFromResource(g_hModule, IDB_IB);
  413.     if (!m_pbmIB2)
  414.         m_pbmIB2 = CBitmap::CreateFromResource(g_hModule, IDB_IB2);
  415. }
  416.  
  417. void CDIDeviceActionConfigPage::FreeResources()
  418. {
  419.     if (m_pbmRelAxesGlyph)
  420.         delete m_pbmRelAxesGlyph;
  421.     if (m_pbmAbsAxesGlyph)
  422.         delete m_pbmAbsAxesGlyph;
  423.     if (m_pbmButtonGlyph)
  424.         delete m_pbmButtonGlyph;
  425.     if (m_pbmHatGlyph)
  426.         delete m_pbmHatGlyph;
  427.     if (m_pbmCheckGlyph)
  428.         delete m_pbmCheckGlyph;
  429.     if (m_pbmCheckGlyphDark)
  430.         delete m_pbmCheckGlyphDark;
  431.     if (m_pbmIB)
  432.         delete m_pbmIB;
  433.     if (m_pbmIB2)
  434.         delete m_pbmIB2;
  435.     m_pbmRelAxesGlyph = NULL;
  436.     m_pbmAbsAxesGlyph = NULL;
  437.     m_pbmButtonGlyph = NULL;
  438.     m_pbmHatGlyph = NULL;
  439.     m_pbmCheckGlyph = NULL;
  440.     m_pbmCheckGlyphDark = NULL;
  441.     m_pbmIB = NULL;
  442.     m_pbmIB2 = NULL;
  443. }
  444.  
  445. void CDIDeviceActionConfigPage::ClearTree()
  446. {
  447.     m_Tree.FreeAll();
  448.     m_pRelAxesParent = NULL;
  449.     m_pAbsAxesParent = NULL;
  450.     m_pButtonParent = NULL;
  451.     m_pHatParent = NULL;
  452.     m_pUnknownParent = NULL;
  453.     m_dwLastControlType = 0;
  454. }
  455.  
  456. void CDIDeviceActionConfigPage::InitTree(BOOL bForceInit)
  457. {
  458.     // get type of control
  459.     DWORD dwControlType = 0;
  460.     if (m_pCurControl && m_pCurControl->IsOffsetAssigned())
  461.     {
  462.         DWORD dwObjId = m_pCurControl->GetOffset();
  463.  
  464.         if (dwObjId & DIDFT_RELAXIS)
  465.             dwControlType = DIDFT_RELAXIS;
  466.         else if (dwObjId & DIDFT_ABSAXIS)
  467.             dwControlType = DIDFT_ABSAXIS;
  468.         else if (dwObjId & DIDFT_BUTTON)
  469.             dwControlType = DIDFT_BUTTON;
  470.         else if (dwObjId & DIDFT_POV)
  471.             dwControlType = DIDFT_POV;
  472.     }
  473.  
  474.     // Turn off the tree's readonly flag if we are in the assign state.
  475.     // We will turn it on later if current control's action has DIA_APPFIXED.
  476.     if (m_State == CFGSTATE_NORMAL)
  477.         m_Tree.SetReadOnly(TRUE);
  478.     else
  479.         m_Tree.SetReadOnly(FALSE);
  480.  
  481.     // if this control type is the same as the last, do nothing,
  482.     // unless we're force init
  483.     if (m_dwLastControlType == dwControlType && !bForceInit && m_State)
  484.         return;
  485.  
  486.     // delete the whole tree
  487.     ClearTree();
  488.  
  489.     // can't use tree if there is no diac or action array
  490.     if (m_lpDiac == NULL || m_lpDiac->rgoAction == NULL)
  491.         return;
  492.  
  493.     // also can't use if we don't have a control type
  494.     if (dwControlType == 0)
  495.         return;
  496.  
  497.     // prepare margin rects
  498.     RECT labelmargin = {14, 6, 3, 3};
  499.     RECT itemmargin = {14, 1, 3, 2};
  500.  
  501.     // set default indents
  502.     m_Tree.SetRootChildIndent(5);
  503.     m_Tree.SetDefChildIndent(12);
  504.  
  505.     // add the control type sections
  506.     m_Tree.SetDefMargin(labelmargin);
  507.     TCHAR tszResourceString[MAX_PATH];
  508.     switch (dwControlType)
  509.     {
  510.         case DIDFT_RELAXIS:
  511.             LoadString(g_hModule, IDS_AXISACTIONS, tszResourceString, MAX_PATH);
  512.             m_pRelAxesParent = m_Tree.DefAddItem(tszResourceString);
  513.             break;
  514.  
  515.         case DIDFT_ABSAXIS:
  516.             LoadString(g_hModule, IDS_AXISACTIONS, tszResourceString, MAX_PATH);
  517.             m_pAbsAxesParent = m_Tree.DefAddItem(tszResourceString);
  518.             break;
  519.  
  520.         case DIDFT_BUTTON:
  521.             LoadString(g_hModule, IDS_BUTTONACTIONS, tszResourceString, MAX_PATH);
  522.             m_pButtonParent = m_Tree.DefAddItem(tszResourceString);
  523.             break;
  524.  
  525.         case DIDFT_POV:
  526.             LoadString(g_hModule, IDS_POVACTIONS, tszResourceString, MAX_PATH);
  527.             m_pHatParent = m_Tree.DefAddItem(tszResourceString);
  528.             break;
  529.  
  530.         default:
  531.             return;
  532.     }
  533.  
  534.     // populate the tree
  535.     m_Tree.SetDefMargin(itemmargin);
  536.     for (unsigned int i = 0; i < m_lpDiac->dwNumActions; i++)
  537.     {
  538.         DIACTIONW *pAction = m_lpDiac->rgoAction + i;
  539.         CFTItem *pItem = NULL;
  540.  
  541.         if (pAction == NULL) 
  542.             continue;
  543.  
  544.         switch (pAction->dwSemantic & DISEM_TYPE_MASK)
  545.         {
  546.             case DISEM_TYPE_AXIS:
  547.                 // Must distinguish between relative and absolute
  548.                 switch((pAction->dwSemantic & DISEM_REL_MASK) >> DISEM_REL_SHIFT)
  549.                 {
  550.                     case 0: pItem = m_pAbsAxesParent; break;
  551.                     case 1: pItem = m_pRelAxesParent; break;
  552.                 }
  553.                 break;
  554.             case DISEM_TYPE_BUTTON: pItem = m_pButtonParent; break;
  555.             case DISEM_TYPE_POV: pItem = m_pHatParent; break;
  556.         }
  557.  
  558.         if (pItem == NULL)
  559.             continue;
  560.  
  561.         // Add action with this name
  562.         CFTItem *pAlready = GetItemWithActionNameAndSemType(pAction->lptszActionName, pAction->dwSemantic);
  563.         if (!pAlready)
  564.         {
  565.             LPTSTR acname = AllocLPTSTR(pAction->lptszActionName);
  566.             pItem = m_Tree.DefAddItem(acname, pItem, ATTACH_LASTCHILD);  // This might return NULL.
  567.             free(acname);
  568.             if (pItem)
  569.                 pItem->SetUserData((LPVOID)(new RGLPDIACW));
  570.         }
  571.         else
  572.         {
  573.             pItem = pAlready;
  574.         }
  575.  
  576.         if (pItem == NULL)
  577.             continue;
  578.  
  579.         pItem->SetUserGUID(GUID_ActionItem);
  580.         RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData();
  581.         if (pacs)
  582.             pacs->SetAtGrow(pacs->GetSize(), pAction);
  583.  
  584.         if (pAlready)
  585.         {
  586.             // The tree already has an action with this name.  Check the DIA_APPFIXED flag for each DIACTION
  587.             // that this item holds.
  588.             DWORD dwNumActions = GetNumItemLpacs(pItem);
  589.             for (DWORD i = 0; i < dwNumActions; ++i)
  590.             {
  591.                 LPDIACTIONW lpExistingAc = GetItemLpac(pItem, i);
  592.                 // If the DIACTION that is assigned to this device has DIA_APPFIXED flag, then
  593.                 //   the other must have it too.
  594.                 if (lpExistingAc && IsEqualGUID(lpExistingAc->guidInstance, m_didi.guidInstance))
  595.                 {
  596.                     if (lpExistingAc->dwFlags & DIA_APPFIXED)
  597.                     {
  598.                         // If this DIACTION has DIA_APPFIXED, then all DIACTIONs must have it too.
  599.                         for (DWORD j = 0; j < dwNumActions; ++j)
  600.                         {
  601.                             LPDIACTIONW lpChangeAc = GetItemLpac(pItem, j);
  602.                             if (lpChangeAc)
  603.                                 lpChangeAc->dwFlags |= DIA_APPFIXED;
  604.                         }
  605.                     }
  606.  
  607.                     break;  // Break the loop since we already found the DIACTION that is assigned.
  608.                 }
  609.             }
  610.         }  // if (pAlready)
  611.     }
  612.  
  613.     // show all
  614.     m_Tree.GetRoot()->ExpandAll();
  615.     m_dwLastControlType = dwControlType;
  616. }
  617.  
  618. int CompareActionNames(LPCWSTR acname1, LPCWSTR acname2)
  619. {
  620. #ifdef CFGUI__COMPAREACTIONNAMES_CASE_INSENSITIVE
  621.     return _wcsicmp(acname1, acname2);
  622. #else
  623.     return wcscmp(acname1, acname2);
  624. #endif
  625. }
  626.  
  627. CFTItem *CDIDeviceActionConfigPage::GetItemWithActionNameAndSemType(LPCWSTR acname, DWORD dwSemantic)
  628. {
  629.     CFTItem *pItem = m_Tree.GetFirstItem();
  630.     for (; pItem != NULL; pItem = pItem->GetNext())
  631.     {
  632.         if (!pItem->IsUserGUID(GUID_ActionItem))
  633.             continue;
  634.         
  635.         LPDIACTIONW lpac = GetItemLpac(pItem);
  636.         if (!lpac)
  637.             continue;
  638.  
  639.         // Check semantic type
  640.         if ((lpac->dwSemantic & DISEM_TYPE_MASK) != (dwSemantic & DISEM_TYPE_MASK))
  641.             continue;
  642.  
  643.         // If both are axis, check for relative/absolute
  644.         if ((lpac->dwSemantic & DISEM_TYPE_MASK) == DISEM_TYPE_AXIS)
  645.             if ((lpac->dwSemantic & DISEM_REL_MASK) != (dwSemantic & DISEM_REL_MASK))
  646.                 continue;
  647.  
  648.         // Check name
  649.         if (CompareActionNames(lpac->lptszActionName, acname) == 0)
  650.             return pItem;
  651.     }
  652.  
  653.     return NULL;
  654. }
  655.  
  656. void CDIDeviceActionConfigPage::OnPaint(HDC hDC)
  657. {
  658.     TCHAR tszResourceString[MAX_PATH];
  659.     CPaintHelper ph(*m_puig, hDC);
  660.  
  661.     ph.SetBrush(UIB_BLACK);
  662.     RECT rect;
  663.     GetClientRect(&rect);
  664.     ph.Rectangle(rect, UIR_SOLID);
  665.  
  666.     ph.SetText(UIC_BORDER, UIC_BLACK);
  667.     {
  668.         rect = g_UserNamesTitleRect;
  669.         LoadString(g_hModule, IDS_PLAYER_TITLE, tszResourceString, MAX_PATH);
  670.         DrawText(hDC, tszResourceString, -1, &rect, DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
  671.     }
  672.     if (m_puig->GetNumMasterAcFors() > 1)
  673.     {
  674.         rect = g_GenresTitleRect;
  675.         LoadString(g_hModule, IDS_GENRE_TITLE, tszResourceString, MAX_PATH);
  676.         DrawText(hDC, tszResourceString, -1, &rect, DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
  677.     }
  678.  
  679.     // Draw tree window title and outline if we are in edit mode.
  680.     if (m_puig->InEditMode())
  681.     {
  682.         COLORREF BorderColor = m_puig->GetColor(UIC_BORDER);
  683.         if (m_Tree.GetReadOnly())
  684.             BorderColor = RGB(GetRValue(BorderColor)>>1, GetGValue(BorderColor)>>1, GetBValue(BorderColor)>>1);
  685.  
  686.         ::SetTextColor(hDC, BorderColor);  // Use the muted color if tree is read only.
  687.         // Draw tree window title (Available Actions)
  688.         rect = g_TreeTitleRect;
  689.         LoadString(g_hModule, IDS_AVAILABLEACTIONS_TITLE, tszResourceString, MAX_PATH);
  690.         DrawText(hDC, tszResourceString, -1, &rect, DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
  691.         // Draw tree window outline
  692.         HGDIOBJ hPen, hOldPen;
  693.         if (m_Tree.GetReadOnly())
  694.         {
  695.             hPen = CreatePen(PS_SOLID, 0, BorderColor);
  696.             hOldPen = ::SelectObject(hDC, hPen);
  697.         }
  698.         else
  699.             ph.SetPen(UIP_BORDER);
  700.  
  701.         RECT rc = g_TreeRect;
  702.         InflateRect(&rc, 1, 1);
  703.         Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
  704.         if (m_Tree.GetReadOnly())
  705.         {
  706.             ::SelectObject(hDC, hOldPen);
  707.             DeleteObject(hPen);
  708.         }
  709.     }
  710.  
  711.     if (m_pDeviceUI->GetNumViews() < 2)
  712.         return;
  713.  
  714.     if (m_pbmIB != NULL)
  715.         m_pbmIB->Draw(hDC, m_ptIBOffset);
  716.     if (m_pbmIB2 != NULL)
  717.         m_pbmIB2->Draw(hDC, m_ptIBOffset2);
  718.     if (m_tszIBText != NULL)
  719.     {
  720.         ph.SetElement(UIE_VIEWSEL);
  721.         RECT rect = m_rectIBText;
  722.         DrawText(hDC, m_tszIBText, -1, &rect, DT_NOCLIP | DT_NOPREFIX);
  723.     }
  724. }
  725.  
  726. void CDIDeviceActionConfigPage::SetCurrentControl(CDeviceControl *pControl)
  727. {
  728.     // If the new control is the same as the old, no need to do anything.
  729.     if (m_pCurControl == pControl)
  730.         return;
  731.     if (m_pCurControl != NULL)
  732.     {
  733.         m_pCurControl->Unhighlight();
  734.         // If we don't have a current control, then invalidate the view so that the old callout can be repainted.
  735.         // If there is a current control, the view will be invalidated by Highlight().
  736.         if (!pControl)
  737.             m_pCurControl->Invalidate();
  738.     }
  739.     m_pCurControl = pControl;
  740.     if (m_pCurControl != NULL)
  741.         m_pCurControl->Highlight();
  742.     ShowCurrentControlAssignment();
  743. }
  744.  
  745. CFTItem *CDIDeviceActionConfigPage::GetItemForActionAssignedToControl(CDeviceControl *pControl)
  746. {
  747.     if (!pControl)
  748.         return NULL;
  749.  
  750.     // find the item for the action assigned to this control, if any
  751.     CFTItem *pItem = m_Tree.GetFirstItem();
  752.     for (; pItem != NULL; pItem = pItem->GetNext())
  753.     {
  754.         if (!pItem->IsUserGUID(GUID_ActionItem))
  755.             continue;
  756.  
  757.         for (int i = 0, n = GetNumItemLpacs(pItem); i < n; i++)
  758.         {
  759.             LPDIACTIONW lpac = GetItemLpac(pItem, i);
  760.             if (!lpac)
  761.                 continue;
  762.  
  763.             if (IsEqualGUID(lpac->guidInstance, m_didi.guidInstance) &&
  764.                     GetOffset(lpac) == pControl->GetOffset())
  765.                 return pItem;
  766.         }
  767.     }
  768.  
  769.     return NULL;
  770. }
  771.  
  772. int CDIDeviceActionConfigPage::GetNumItemLpacs(CFTItem *pItem)
  773. {
  774.     if (pItem == NULL)
  775.         return 0;
  776.  
  777.     RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData();
  778.     if (!pacs)
  779.         return 0;
  780.     else
  781.         return pacs->GetSize();
  782. }
  783.  
  784. LPDIACTIONW CDIDeviceActionConfigPage::GetItemLpac(CFTItem *pItem, int i)
  785. {
  786.     if (pItem == NULL)
  787.         return NULL;
  788.  
  789.     RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData();
  790.     if (!pacs || i < 0 || i >= pacs->GetSize())
  791.         return NULL;
  792.     else    
  793.         return pacs->GetAt(i);
  794. }
  795.  
  796. void CDIDeviceActionConfigPage::ShowCurrentControlAssignment()
  797. {
  798.     // init the tree
  799.     InitTree();
  800.  
  801.     // if we don't have a control...
  802.     if (m_pCurControl == NULL)
  803.     {
  804.         // select nothing
  805.         m_Tree.SetCurSel(NULL);
  806.         return;
  807.     }
  808.  
  809.     // find the item for the action assigned to this control, if any
  810.     CFTItem *pItem = GetItemForActionAssignedToControl(m_pCurControl);
  811.  
  812.     // if we didn't find a match...
  813.     if (!pItem)
  814.     {
  815.         // select nothing
  816.         m_Tree.SetCurSel(NULL);
  817.         return;
  818.     }
  819.  
  820.     // We need to check if the action this control is assigned to has DIA_APPFIXED flag.
  821.     // If it does, this control cannot be remapped to another action.
  822.     // We prevent this by setting the tree control to read-only, so it can't receive any clicks.
  823.     LPDIACTIONW lpAc = GetItemLpac(pItem);  // Get the action
  824.     if (lpAc && (lpAc->dwFlags & DIA_APPFIXED))
  825.         m_Tree.SetReadOnly(TRUE);
  826.  
  827.     // otherwise, show item and select it
  828.     pItem->EnsureVisible();
  829.     m_Tree.SetCurSel(pItem);
  830. }
  831.  
  832. void CDIDeviceActionConfigPage::DeviceUINotify(const DEVICEUINOTIFY &uin)
  833. {
  834.     switch (uin.msg)
  835.     {
  836.         case DEVUINM_NUMVIEWSCHANGED:
  837.             Invalidate();
  838.             break;
  839.  
  840.         case DEVUINM_SELVIEW:
  841.             // set the view
  842.             m_pDeviceUI->SetView(uin.selview.nView);
  843.  
  844.             // show the assignments for the controls
  845.             SetControlAssignments();
  846.  
  847.             // select nothing
  848.             SetCurrentControl(NULL);
  849.             break;
  850.  
  851.         case DEVUINM_ONCONTROLDESTROY:
  852.             if (uin.control.pControl == m_pCurControl)
  853.                 m_pCurControl = NULL;
  854.             break;
  855.  
  856.         case DEVUINM_CLICK:
  857.             ExitAssignState();
  858.             switch (uin.from)
  859.             {
  860.                 case DEVUINFROM_CONTROL:
  861.                     SetCurrentControl(uin.control.pControl);
  862.                     SetAppropriateDefaultText();
  863.                     break;
  864.                 case DEVUINFROM_VIEWWND:
  865.                     break;
  866.             }
  867.             break;
  868.  
  869.         case DEVUINM_DOUBLECLICK:
  870.             switch (uin.from)
  871.             {
  872.                 case DEVUINFROM_CONTROL:
  873.                     EnterAssignState();
  874.                     break;
  875.             }
  876.             break;
  877.  
  878.         case DEVUINM_MOUSEOVER:
  879.             SetAppropriateDefaultText();
  880.             break;
  881.  
  882.         case DEVUINM_RENEWDEVICE:
  883.             HWND hParent = GetParent(m_hWnd);
  884.             CConfigWnd *pCfgWnd = (CConfigWnd *)GetFlexWnd(hParent);
  885.             if (pCfgWnd)
  886.             {
  887.                 LPDIRECTINPUTDEVICE8W lpDID = pCfgWnd->RenewDevice(m_didi.guidInstance);
  888.                 if (lpDID)
  889.                 {
  890.                     // Destroy the device instance we have
  891.                     if (m_lpDID) m_lpDID->Release();
  892.                     lpDID->AddRef();
  893.                     m_lpDID = lpDID;
  894.                 }
  895.                 m_pDeviceUI->SetDevice(lpDID);  // Sets the device pointer in CDeviceUI (no need to AddRef)
  896.             }
  897.     }
  898. }
  899.  
  900. void CDIDeviceActionConfigPage::UnassignCallout()
  901. {
  902.     // find the item for the action assigned to this control, if any
  903.     CFTItem *pItem = GetItemForActionAssignedToControl(m_pCurControl);
  904.     if (pItem)
  905.     {
  906.         LPDIACTIONW lpac = GetItemLpac(pItem);
  907.         // Only unassign if the action doesn't have DIA_APPFIXED flag.
  908.         if (lpac && !(lpac->dwFlags & DIA_APPFIXED))
  909.         {
  910.             ActionClick(NULL);
  911.             m_Tree.Invalidate();
  912.         }
  913.     }
  914.     // Sort the list if the check box is checked.
  915.     if (m_CheckBox.GetCheck())
  916.         m_pDeviceUI->GetCurView()->SortAssigned(TRUE);
  917. }
  918.  
  919. void CDIDeviceActionConfigPage::NullAction(LPDIACTIONW lpac)
  920. {
  921.     if (lpac == NULL)
  922.         return;
  923.  
  924.     SetInvalid(lpac);
  925.  
  926. }
  927.  
  928. void CDIDeviceActionConfigPage::UnassignActionsAssignedTo(const GUID &guidInstance, DWORD dwOffset)
  929. {
  930.     if (m_lpDiac == NULL || m_lpDiac->rgoAction == NULL)
  931.         return;
  932.  
  933.     if (IsEqualGUID(guidInstance, GUID_NULL))
  934.         return;
  935.  
  936.     // assign any actions assigned to this control to nothing
  937.     DWORD i;
  938.     LPDIACTIONW lpac;
  939.     for (i = 0, lpac = m_lpDiac->rgoAction; i < m_lpDiac->dwNumActions; i++, lpac++)
  940.         if (IsEqualGUID(guidInstance, lpac->guidInstance) && dwOffset == GetOffset(lpac)/*->dwInternalOffset*/)
  941.         {
  942.             GlobalUnassignControlAt(guidInstance, dwOffset);
  943.             NullAction(lpac);
  944.         }
  945. }
  946.  
  947. void CDIDeviceActionConfigPage::UnassignControl(CDeviceControl *pControl)
  948. {
  949.     if (pControl == NULL)
  950.         return;
  951.  
  952.     // make sure the control itself indicates unassignment
  953.     pControl->SetCaption(g_tszUnassignedControlCaption);
  954. }
  955.  
  956. void CallUnassignControl(CDeviceControl *pControl, LPVOID pVoid, BOOL bFixed)
  957. {
  958.     CDIDeviceActionConfigPage *pThis = (CDIDeviceActionConfigPage *)pVoid;
  959.     pThis->UnassignControl(pControl);
  960. }
  961.  
  962. void CDIDeviceActionConfigPage::GlobalUnassignControlAt(const GUID &guidInstance, DWORD dwOffset)
  963. {
  964.     if (IsEqualGUID(guidInstance, GUID_NULL))
  965.         return;
  966.  
  967.     if (IsEqualGUID(guidInstance, m_didi.guidInstance))
  968.         m_pDeviceUI->DoForAllControlsAtOffset(dwOffset, CallUnassignControl, this);
  969. }
  970.  
  971. // this function must find whatever control is assigned to this action and unassign it
  972. void CDIDeviceActionConfigPage::UnassignAction(LPDIACTIONW slpac)
  973. {
  974.     // call UnassignSpecificAction for each action with the same name
  975.     // as this one, including this one
  976.     
  977.     if (slpac == NULL)
  978.         return;
  979.  
  980.     CFTItem *pItem = GetItemWithActionNameAndSemType(slpac->lptszActionName, slpac->dwSemantic);
  981.     if (!pItem)
  982.         return;
  983.  
  984.     RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData();
  985.     if (!pacs)
  986.         return;
  987.  
  988.     for (int i = 0; i < pacs->GetSize(); i++)
  989.         UnassignSpecificAction(pacs->GetAt(i));
  990. }
  991.  
  992. void CDIDeviceActionConfigPage::UnassignSpecificAction(LPDIACTIONW lpac)
  993. {
  994.     if (lpac == NULL)
  995.         return;
  996.  
  997.     if (IsEqualGUID(lpac->guidInstance, GUID_NULL))
  998.         return;
  999.  
  1000.     // if there's a control with this instance/offset, unassign it
  1001.     UnassignActionsAssignedTo(lpac->guidInstance, GetOffset(lpac)/*->dwInternalOffset*/);
  1002.     GlobalUnassignControlAt(lpac->guidInstance, GetOffset(lpac)/*->dwInternalOffset*/);
  1003.  
  1004.     // now actually null the action
  1005.     NullAction(lpac);
  1006. }
  1007.  
  1008. void CDIDeviceActionConfigPage::AssignCurrentControlToAction(LPDIACTIONW lpac)
  1009. {
  1010.     // if there is a control, unassign it
  1011.     if (m_pCurControl != NULL)
  1012.     {
  1013.         UnassignControl(m_pCurControl);
  1014.         GUID guidInstance;
  1015.         DWORD dwOffset;
  1016.         m_pCurControl->GetInfo(guidInstance, dwOffset);
  1017.         UnassignActionsAssignedTo(guidInstance, dwOffset);
  1018.     }
  1019.  
  1020.     // if there is an action, unassign it
  1021.     if (lpac != NULL)
  1022.         UnassignAction(lpac);
  1023.  
  1024.     // can only continue if we have both
  1025.     if (lpac == NULL || m_pCurControl == NULL)
  1026.         return;
  1027.  
  1028.     // here we should have a control and an action
  1029.     assert(lpac != NULL);
  1030.     assert(m_pCurControl != NULL);
  1031.  
  1032.     // because an action can only be assigned to one control,
  1033.     // make sure this action is unassigned first
  1034.     UnassignAction(lpac);
  1035.  
  1036.     // now actually assign
  1037.     DWORD ofs;
  1038.     m_pCurControl->GetInfo(lpac->guidInstance, ofs/*lpac->dwInternalOffset*/);
  1039.     SetOffset(lpac, ofs);
  1040.     LPTSTR acname = AllocLPTSTR(lpac->lptszActionName);
  1041.     m_pCurControl->SetCaption(acname, lpac->dwFlags & DIA_APPFIXED);
  1042.     free(acname);
  1043.  
  1044.     // Sort the action list if check box is checked
  1045.     if (m_CheckBox.GetCheck())
  1046.     {
  1047.         m_pDeviceUI->GetCurView()->SortAssigned(TRUE);
  1048.         // Scroll so that we scroll to make this visible since it might be displaced by sorting.
  1049.         m_pDeviceUI->GetCurView()->ScrollToMakeControlVisible(m_pCurControl->GetCalloutMaxRect());
  1050.     }
  1051. }
  1052.  
  1053. void CDIDeviceActionConfigPage::ActionClick(LPDIACTIONW lpac)
  1054. {
  1055.     if (m_pCurControl != NULL)
  1056.     {
  1057.         AssignCurrentControlToAction(lpac);
  1058.  
  1059.         // Set assignment since other views may have the same callout and
  1060.         // they need to be updated too.
  1061.         SetControlAssignments();
  1062.     }
  1063.     // Change the state back to normal
  1064.     ExitAssignState();
  1065. }
  1066.  
  1067. void CDIDeviceActionConfigPage::SetControlAssignments()
  1068. {
  1069.     assert(!IsEqualGUID(m_didi.guidInstance, GUID_NULL));
  1070.  
  1071.     m_pDeviceUI->SetAllControlCaptionsTo(g_tszUnassignedControlCaption);
  1072.  
  1073.     if (m_lpDiac == NULL || m_lpDiac->rgoAction == NULL)
  1074.         return;
  1075.  
  1076.     DWORD i;
  1077.     LPDIACTIONW lpac;
  1078.     for (i = 0, lpac = m_lpDiac->rgoAction; i < m_lpDiac->dwNumActions; i++)
  1079.     {
  1080.         lpac = m_lpDiac->rgoAction + i;
  1081.  
  1082.         if (IsEqualGUID(lpac->guidInstance, GUID_NULL))
  1083.             continue;
  1084.  
  1085.         if (!IsEqualGUID(lpac->guidInstance, m_didi.guidInstance))
  1086.             continue;
  1087.  
  1088.         LPTSTR acname = AllocLPTSTR(lpac->lptszActionName);
  1089.         m_pDeviceUI->SetCaptionForControlsAtOffset(GetOffset(lpac)/*->dwInternalOffset*/, acname, lpac->dwFlags & DIA_APPFIXED);
  1090.         free(acname);
  1091.     }
  1092. }
  1093.  
  1094. void CDIDeviceActionConfigPage::DoViewSel()
  1095. {
  1096.     m_ViewSelWnd.Go(m_hWnd, m_rectIB.left, m_rectIB.top, m_pDeviceUI);
  1097. }
  1098.  
  1099. void CDIDeviceActionConfigPage::OnClick(POINT point, WPARAM, BOOL bLeft)
  1100. {
  1101.     if (!bLeft)
  1102.         return;
  1103.  
  1104.     // Unhighlight current callout
  1105.     ExitAssignState();
  1106.  
  1107.     if (m_pDeviceUI->GetNumViews() > 1)
  1108.     {
  1109.         int iCurView = m_pDeviceUI->GetCurViewIndex();
  1110.  
  1111.         if (PtInRect(&m_rectIBLeft, point))
  1112.             m_pDeviceUI->SetView(iCurView == 0 ? m_pDeviceUI->GetNumViews() - 1 : iCurView - 1);
  1113.         if (PtInRect(&m_rectIBRight, point))
  1114.             m_pDeviceUI->SetView(iCurView == m_pDeviceUI->GetNumViews() - 1 ? 0 : iCurView + 1);
  1115.         if (PtInRect(&m_rectIBText, point))
  1116.             DoViewSel();
  1117.     }
  1118. }
  1119.  
  1120. void CDIDeviceActionConfigPage::OnMouseOver(POINT point, WPARAM fwKeys)
  1121. {
  1122.     CFlexWnd::s_ToolTip.SetEnable(FALSE);
  1123.  
  1124.     // Check view selection area so we can display text in info box.
  1125.     if (m_pDeviceUI->GetNumViews() > 1)
  1126.     {
  1127.         if (PtInRect(&m_rectIB, point))
  1128.         {
  1129.             SetInfoText(IDS_INFOMSG_VIEW_VIEWSEL);
  1130.             return;
  1131.         }
  1132.     }
  1133.  
  1134.     SetAppropriateDefaultText();
  1135. }
  1136.  
  1137. int GetActionIndexFromPointer(LPDIACTIONW p, LPDIACTIONFORMATW paf)
  1138. {
  1139.     if (!p || !paf || !paf->rgoAction)
  1140.         return -1;
  1141.  
  1142.     int index = int((((LPBYTE)p) - ((LPBYTE)paf->rgoAction)) / (DWORD)sizeof(DIACTIONW));
  1143.  
  1144.     assert(&(paf->rgoAction[index]) == p);
  1145.  
  1146.     return index;
  1147. }
  1148.  
  1149. BOOL CDIDeviceActionConfigPage::IsActionAssignedHere(int index)
  1150. {
  1151.     if (!m_lpDiac)
  1152.         return FALSE;
  1153.  
  1154.     if (index < 0 || index >= (int)m_lpDiac->dwNumActions)
  1155.         return FALSE;
  1156.  
  1157.     return IsEqualGUID(m_didi.guidInstance, m_lpDiac->rgoAction[index].guidInstance);
  1158. }
  1159.  
  1160. LRESULT CDIDeviceActionConfigPage::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1161. {
  1162.     switch (msg)
  1163.     {
  1164.         case WM_UNHIGHLIGHT:
  1165.             // Unhighlight current callout
  1166.             ExitAssignState();
  1167.             break;
  1168.  
  1169.         case WM_KEYDOWN:
  1170. #ifdef DBG
  1171.             // In debug version, shift-escape exits the UI.
  1172.             if (wParam == VK_ESCAPE && GetAsyncKeyState(VK_SHIFT) < 0)
  1173.             {
  1174.                 PostMessage(GetParent(m_hWnd), WM_KEYDOWN, wParam, lParam);
  1175.                 break;
  1176.             }
  1177. #endif
  1178.             // If this is a keyboard device, then click-to-pick will take care of the functionalities below.
  1179.             // Process WM_KEYDOWN only for non-keyboard devices.
  1180.             if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_KEYBOARD) return 0;
  1181.             switch(wParam)
  1182.             {
  1183.                 case VK_RETURN:
  1184.                     // If we are not in assign state, enter it.
  1185.                     if (m_State == CFGSTATE_NORMAL && m_pCurControl)
  1186.                         EnterAssignState();
  1187.                     break;
  1188.  
  1189.                 case VK_DELETE:
  1190.                     // If we are in assign state and there is a control, unassign it.
  1191.                     if (m_State == CFGSTATE_ASSIGN && m_pCurControl)
  1192.                         UnassignCallout();
  1193.                     break;
  1194.  
  1195.                 case VK_ESCAPE:
  1196.                     if (m_State == CFGSTATE_ASSIGN)
  1197.                         ExitAssignState();
  1198.  
  1199.                     break;
  1200.             }
  1201.             return 0;
  1202.  
  1203.         case WM_FLEXCHECKBOX:
  1204.             switch(wParam)
  1205.             {
  1206.                 case CHKNOTIFY_UNCHECK:
  1207.                         m_pDeviceUI->GetCurView()->SortAssigned(FALSE);
  1208.                         if (m_pCurControl)
  1209.                         {
  1210.                             // Scroll so that we scroll to make this visible since it might be displaced by sorting.
  1211.                             m_pDeviceUI->GetCurView()->ScrollToMakeControlVisible(m_pCurControl->GetCalloutMaxRect());
  1212.                         }
  1213.                         Invalidate();
  1214.                     break;
  1215.                 case CHKNOTIFY_CHECK:
  1216.                         m_pDeviceUI->GetCurView()->SortAssigned(TRUE);
  1217.                         if (m_pCurControl)
  1218.                         {
  1219.                             // Scroll so that we scroll to make this visible since it might be displaced by sorting.
  1220.                             m_pDeviceUI->GetCurView()->ScrollToMakeControlVisible(m_pCurControl->GetCalloutMaxRect());
  1221.                         }
  1222.                         Invalidate();
  1223.                     break;
  1224.                 case CHKNOTIFY_MOUSEOVER:
  1225.                     SetInfoText(m_CheckBox.GetCheck() ? IDS_INFOMSG_VIEW_SORTENABLED : IDS_INFOMSG_VIEW_SORTDISABLED);
  1226.                     break;
  1227.             }
  1228.             break;
  1229.  
  1230.         case WM_FLEXCOMBOBOX:
  1231.             switch (wParam)
  1232.             {
  1233.                 case FCBN_MOUSEOVER:
  1234.                     if (lParam)
  1235.                     {
  1236.                         CFlexComboBox *pCombo = (CFlexComboBox*)lParam;
  1237.                         if (pCombo->m_hWnd == m_UserNames.m_hWnd)
  1238.                             SetInfoText(m_puig->InEditMode() ? IDS_INFOMSG_EDIT_USERNAME : IDS_INFOMSG_VIEW_USERNAME);
  1239.                         else if (pCombo->m_hWnd == m_Genres.m_hWnd)
  1240.                             SetInfoText(m_puig->InEditMode() ? IDS_INFOMSG_EDIT_GAMEMODE : IDS_INFOMSG_VIEW_GAMEMODE);
  1241.                     }
  1242.                     break;
  1243.  
  1244.                 case FCBN_SELCHANGE:
  1245.                     // Clear the tool tip as the combo-box has closed
  1246.                     CFlexWnd::s_ToolTip.SetEnable(FALSE);
  1247.                     CFlexWnd::s_ToolTip.SetToolTipParent(NULL);
  1248.                     if (m_pUIFrame && m_puig)
  1249.                     {
  1250.                         ExitAssignState();
  1251.                         m_pUIFrame->SetCurGenre(m_Genres.GetSel());
  1252.                         int nUser = m_UserNames.GetSel();
  1253.                         if (m_puig->GetNumUserNames() > 0 && nUser >= m_puig->GetNumUserNames())
  1254.                             nUser = -1;
  1255.                         m_pUIFrame->SetCurUser(m_nPageIndex, nUser);
  1256.                     }
  1257.                     break;
  1258.             }
  1259.             return 0;
  1260.  
  1261.         case WM_FLEXTREENOTIFY:
  1262.         {
  1263.             // Check if this is a mouse over message (just for info box update)
  1264.             if (wParam == FTN_MOUSEOVER)
  1265.             {
  1266.                 SetAppropriateDefaultText();
  1267.                 return FALSE;
  1268.             }
  1269.  
  1270.             if (!lParam)
  1271.                 return FALSE;
  1272.             FLEXTREENOTIFY &n = *((FLEXTREENOTIFY *)(LPVOID)lParam);
  1273.             if (!n.pItem)
  1274.                 return FALSE;
  1275.             switch (wParam)
  1276.             {
  1277.                 case FTN_OWNERDRAW:
  1278.                 {
  1279.                     POINT ofs = {0, 0};
  1280.                     CBitmap *pbmGlyph = NULL;
  1281.                     BOOL bAssigned = FALSE, bAssignedHere = FALSE;
  1282.                     if (n.pItem->IsUserGUID(GUID_ActionItem))
  1283.                     {
  1284.                         LPDIACTIONW lpac = GetItemLpac(n.pItem, 0);
  1285.                         if (lpac)
  1286.                             // We now walk through each DIACTION and find those with action name match, then see if 
  1287.                             // they are assigned anywhere.
  1288.                             for (DWORD i = 0; i < m_lpDiac->dwNumActions; ++i)
  1289.                             {
  1290.                                 if (wcscmp(lpac->lptszActionName, m_lpDiac->rgoAction[i].lptszActionName))
  1291.                                     continue;
  1292.  
  1293.                                 if (bAssignedHere = IsActionAssignedHere(i))
  1294.                                 {
  1295.                                     bAssigned = TRUE;
  1296.                                     break;
  1297.                                 }
  1298.                                 if (m_pUIFrame && m_pUIFrame->QueryActionAssignedAnywhere(m_didi.guidInstance, i) == S_OK)
  1299.                                     bAssigned = TRUE;
  1300.                             }
  1301.                         if (bAssigned || bAssignedHere)
  1302.                         {
  1303.                             pbmGlyph = bAssignedHere ? m_pbmCheckGlyph :
  1304.                                 m_pbmCheckGlyphDark;
  1305.                             pbmGlyph->FigureSize();
  1306.                             ofs.x = 2;
  1307.                             ofs.y = 4;
  1308.                         }
  1309.                     }
  1310.                     else
  1311.                     {
  1312.                         if (n.pItem == m_pRelAxesParent)
  1313.                             pbmGlyph = m_pbmRelAxesGlyph;
  1314.                         if (n.pItem == m_pAbsAxesParent)
  1315.                             pbmGlyph = m_pbmAbsAxesGlyph;
  1316.                         if (n.pItem == m_pButtonParent)
  1317.                             pbmGlyph = m_pbmButtonGlyph;
  1318.                         if (n.pItem == m_pHatParent)
  1319.                             pbmGlyph = m_pbmHatGlyph;
  1320.                         ofs.y = 2;
  1321.                     }
  1322.                     if (!pbmGlyph)
  1323.                         return FALSE;
  1324.                     n.pItem->PaintInto(n.hDC);
  1325.                     RECT rect;
  1326.                     CPaintHelper ph(*m_puig, n.hDC);
  1327.                     ph.SetElement(UIE_GLYPH);
  1328.                     n.pItem->GetMargin(rect);
  1329.                     pbmGlyph->Draw(n.hDC, ofs.x, rect.top + ofs.y);
  1330.                     return TRUE;
  1331.                 }
  1332.  
  1333.                 case FTN_CLICK:
  1334.                     // We cannot assign a different control to this action if it has the DIA_APPFIXED flag.
  1335.                     if (n.pItem->IsUserGUID(GUID_ActionItem) && GetItemLpac(n.pItem) && !(GetItemLpac(n.pItem)->dwFlags & DIA_APPFIXED))
  1336.                     {
  1337.                         m_Tree.SetCurSel(n.pItem);
  1338.                         ActionClick(GetItemLpac(n.pItem));
  1339.                     }
  1340.                     else
  1341.                     {
  1342. #ifdef CFGUI__ALLOW_USER_ACTION_TREE_BRANCH_MANIPULATION
  1343.                         if (!n.pItem->IsExpanded())
  1344.                             n.pItem->Expand();
  1345.                         else
  1346.                             n.pItem->Collapse();
  1347. #endif
  1348.                     }
  1349.                     break;
  1350.             }
  1351.             break;
  1352.         }
  1353.     }
  1354.  
  1355.     return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  1356. }
  1357.  
  1358. void CDIDeviceActionConfigPage::SetInvalid(LPDIACTIONW lpac)
  1359. {
  1360.     lpac->guidInstance = GUID_NULL;
  1361.     lpac->dwObjID = (DWORD)-1;
  1362. }
  1363.  
  1364. DWORD CDIDeviceActionConfigPage::GetOffset(LPDIACTIONW lpac)
  1365. {
  1366.     return lpac ? lpac->dwObjID : (DWORD)-1;
  1367. }
  1368.  
  1369. void CDIDeviceActionConfigPage::SetOffset(LPDIACTIONW lpac, DWORD ofs)
  1370. {
  1371.     assert(lpac != NULL);
  1372.     if (!lpac)
  1373.         return;
  1374.     lpac->dwObjID = ofs;
  1375. }
  1376.  
  1377. HRESULT CDIDeviceActionConfigPage::InitLookup()
  1378. {
  1379.     DIDEVOBJSTRUCT os;
  1380.  
  1381.     HRESULT hresult = FillDIDeviceObjectStruct(os, m_lpDID);
  1382.  
  1383.     if (FAILED(hresult))
  1384.         return hresult;
  1385.  
  1386.     for (int i = 0; i < os.nObjects; i++)
  1387.     {
  1388.         DIDEVICEOBJECTINSTANCEW &doi = os.pdoi[i];
  1389.         offset_objid.add(doi.dwOfs, doi.dwType);
  1390.     }
  1391.  
  1392.     return S_OK;
  1393. }
  1394.  
  1395. HRESULT CDIDeviceActionConfigPage::SetEditLayout(BOOL bEditLayout)
  1396. {
  1397.     m_pDeviceUI->SetEditMode(bEditLayout);
  1398.     return S_OK;
  1399. }
  1400.  
  1401.  
  1402. BOOL CDIDeviceActionConfigPage::IsControlMapped(CDeviceControl *pControl)
  1403. {
  1404.     if (pControl == NULL)
  1405.         return FALSE;
  1406.  
  1407.     if (!pControl->IsOffsetAssigned())
  1408.         return FALSE;
  1409.  
  1410.     if (m_lpDiac == NULL)
  1411.         return FALSE;
  1412.     
  1413.     for (DWORD i = 0; i < m_lpDiac->dwNumActions; i++)
  1414.         if (GetOffset(&(m_lpDiac->rgoAction[i])) == pControl->GetOffset())
  1415.             return TRUE;
  1416.  
  1417.     return FALSE;
  1418. }
  1419.  
  1420. void CDIDeviceActionConfigPage::InitDevice()
  1421. {
  1422.     if (m_lpDID == NULL || m_pDeviceUI == NULL || m_pUIFrame == NULL)
  1423.         return;
  1424.  
  1425.     HWND hWndMain = m_pUIFrame->GetMainHWND();
  1426.     if (!hWndMain)
  1427.         return;
  1428.  
  1429.     // don't do anything if this is a mouse
  1430.     switch ((DWORD)(LOBYTE(LOWORD(m_pDeviceUI->m_didi.dwDevType))))
  1431.     {
  1432.         case DI8DEVTYPE_MOUSE:
  1433.             return;
  1434.     }
  1435.  
  1436.     // init/prepare...
  1437.     int i;
  1438.     const DIDEVOBJSTRUCT &os = m_pDeviceUI->m_os;
  1439.     int nObjects = os.nObjects;
  1440.  
  1441.     DIDATAFORMAT df;
  1442.     df.dwSize = sizeof(DIDATAFORMAT);
  1443.     df.dwObjSize = sizeof(DIOBJECTDATAFORMAT);
  1444.     df.dwFlags = DIDF_ABSAXIS;
  1445.     df.dwDataSize = sizeof(DWORD) * (DWORD)nObjects;
  1446.     df.dwNumObjs = (DWORD)nObjects;
  1447.     df.rgodf = (DIOBJECTDATAFORMAT *)malloc(sizeof(DIOBJECTDATAFORMAT) * nObjects);
  1448.     if (df.rgodf == NULL)
  1449.     {
  1450.         etrace1(_T("Could not allocate DIOBJECTDATAFORMAT array of %d elements\n"), nObjects);
  1451.         return;
  1452.     }
  1453.  
  1454.     m_cbDeviceDataSize = df.dwDataSize;
  1455.     for (int c = 0; c < 2; c++)
  1456.     {
  1457.         if (m_pDeviceData[c] != NULL)
  1458.             free(m_pDeviceData[c]);
  1459.         m_pDeviceData[c] = (DWORD *)malloc(m_cbDeviceDataSize);
  1460.         if (m_pDeviceData[c] == NULL)
  1461.             etrace2(_T("Could not allocate device data buffer %d of %d bytes\n"), c, m_cbDeviceDataSize);
  1462.     }
  1463.     m_nOnDeviceData = 0;
  1464.     m_bFirstDeviceData = TRUE;
  1465.  
  1466.     for (i = 0; i < nObjects; i++)
  1467.     {
  1468.         DIOBJECTDATAFORMAT *podf = &(df.rgodf[i]);
  1469.         podf->pguid = NULL;
  1470.         podf->dwOfs = i * sizeof(DWORD);
  1471.         podf->dwType = os.pdoi[i].dwType;
  1472.         podf->dwFlags = 0;
  1473.     }
  1474.  
  1475.     if (df.rgodf != NULL)
  1476.     {
  1477.         HRESULT hr = m_lpDID->SetDataFormat(&df);
  1478.         free(df.rgodf);
  1479.         df.rgodf = NULL;
  1480.  
  1481.         if (FAILED(hr))
  1482.         {
  1483.             etrace1(_T("SetDataFormat() failed, returning 0x%08x\n"), hr);
  1484.         }
  1485.         else
  1486.         {
  1487.             hr = m_lpDID->SetCooperativeLevel(hWndMain, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE);
  1488.             if (FAILED(hr))
  1489.                 etrace1(_T("SetCooperativeLevel() failed, returning 0x%08x\n"), hr);
  1490.  
  1491.             DIPROPRANGE range;
  1492.             range.diph.dwSize = sizeof(DIPROPRANGE);
  1493.             range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1494.             range.diph.dwObj = 0;
  1495.             range.diph.dwHow = DIPH_DEVICE;
  1496.             range.lMin = DEVICE_POLLING_AXIS_MIN;
  1497.             range.lMax = DEVICE_POLLING_AXIS_MAX;
  1498.  
  1499.             hr = m_lpDID->SetProperty(DIPROP_RANGE, (LPCDIPROPHEADER)&range);
  1500.             if (FAILED(hr))
  1501.                 etrace1(_T("SetProperty(DIPROP_RANGE, ...) failed, returning 0x%08x\n"), hr);
  1502.  
  1503.             hr = m_lpDID->Acquire();
  1504.             if (FAILED(hr))
  1505.                 etrace1(_T("Acquire() failed, returning 0x%08x\n"), hr);
  1506.         }
  1507.     }
  1508. }
  1509.  
  1510. void CALLBACK CDIDeviceActionConfigPage::DeviceTimerProc(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
  1511. {
  1512.     if (!IsWindow((HWND)dwUser)) return;  // Verify that dwUser is a valid window handle
  1513.     CDIDeviceActionConfigPage *pPage = (CDIDeviceActionConfigPage *)GetFlexWnd((HWND)dwUser);  // Get flex object
  1514.     if (pPage)
  1515.         pPage->DeviceTimer();
  1516. }
  1517.  
  1518. void CDIDeviceActionConfigPage::DeviceTimer()
  1519. {
  1520.     DWORD *pOldData = m_pDeviceData[m_nOnDeviceData];
  1521.     m_nOnDeviceData = (m_nOnDeviceData + 1) & 1;
  1522.     DWORD *pData = m_pDeviceData[m_nOnDeviceData];
  1523.  
  1524.     if (m_lpDID == NULL || pData == NULL || pOldData == NULL)
  1525.     {
  1526.         // Required data not available.  Return and there'll be no more timer callbacks.
  1527.         etrace(_T("DeviceTimer() failed\n"));
  1528.         return;
  1529.     }
  1530.  
  1531.     // Get device data only if this page is visible.
  1532.     if (m_lpDiac)
  1533.     {
  1534.         HRESULT hr = m_lpDID->Poll();
  1535.         if (SUCCEEDED(hr))
  1536.         {
  1537.             hr = m_lpDID->GetDeviceState(m_cbDeviceDataSize, pData);
  1538.             if (SUCCEEDED(hr))
  1539.             {
  1540.                 if (!m_bFirstDeviceData)
  1541.                 {
  1542.                     DeviceDelta(pData, pOldData);
  1543.                 } else
  1544.                 {
  1545.                     m_bFirstDeviceData = FALSE;
  1546.                 }
  1547.             } else
  1548.             {
  1549.                 etrace1(_T("GetDeviceState() failed, returning 0x%08x\n"), hr);
  1550.             }
  1551.         } else
  1552.         {
  1553.             etrace1(_T("Poll() failed, returning 0x%08x\n"), hr);
  1554.         }
  1555.     }
  1556.  
  1557.     // Set the next timer event.
  1558.     if (g_fptimeSetEvent)
  1559.         g_fptimeSetEvent(DEVICE_POLLING_INTERVAL, DEVICE_POLLING_INTERVAL,
  1560.                          CDIDeviceActionConfigPage::DeviceTimerProc, (DWORD_PTR)m_hWnd, TIME_ONESHOT);
  1561. }
  1562.  
  1563. void CDIDeviceActionConfigPage::DeviceDelta(DWORD *pData, DWORD *pOldData)
  1564. {
  1565.     if (pData == NULL || pOldData == NULL || m_pDeviceUI == NULL)
  1566.         return;
  1567.  
  1568.     const DIDEVOBJSTRUCT &os = m_pDeviceUI->m_os;
  1569.  
  1570.     // see which objects changed
  1571.     for (int i = 0; i < os.nObjects; i++)
  1572.     {
  1573.         // for axes, we need to do special processing
  1574.         if (os.pdoi[i].dwType & DIDFT_AXIS)
  1575.         {
  1576.             BOOL bSig = FALSE, bOldSig = FALSE;
  1577.  
  1578.             StoreAxisDeltaAndCalcSignificance(os.pdoi[i],
  1579.                 pData[i], pOldData[i], bSig, bOldSig);
  1580.  
  1581.             AxisDelta(os.pdoi[i], bSig, bOldSig);
  1582.  
  1583.             continue;
  1584.         }
  1585.  
  1586.         // for all others, skip that which didn't change
  1587.         if (pData[i] == pOldData[i])
  1588.             continue;
  1589.  
  1590.         // pass to appropriate delta function
  1591.         DWORD dwObjId = os.pdoi[i].dwType;
  1592.         if (dwObjId & DIDFT_BUTTON)
  1593.             ButtonDelta(os.pdoi[i], pData[i], pOldData[i]);
  1594.         else if (dwObjId & DIDFT_POV)
  1595.             PovDelta(os.pdoi[i], pData[i], pOldData[i]);
  1596.     }
  1597. }
  1598.  
  1599. void CDIDeviceActionConfigPage::StoreAxisDeltaAndCalcSignificance(const DIDEVICEOBJECTINSTANCEW &doi, DWORD data, DWORD olddata, BOOL &bSig, BOOL &bOldSig)
  1600. {
  1601.     // see if this object has an axis value array
  1602.     int i;
  1603.     if (objid_avai.getright(doi.dwType, i))
  1604.     {
  1605.         AxisValueArray &ar = m_AxisValueArray[i];
  1606.         int on = ar[0] + 1;
  1607.         if (on >= ar.GetSize())
  1608.             on = DEVICE_POLLING_ACBUF_START_INDEX;
  1609.         ar[0] = on;
  1610.         int delta = abs(int(data) - int(olddata));
  1611.         // Scale up the delta if this is a wheel axis as wheels are harder to move generally.
  1612.         if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_DRIVING && doi.guidType == GUID_XAxis)
  1613.             delta = delta * DEVICE_POLLING_WHEEL_SCALE_FACTOR;
  1614.         if (delta < DEVICE_POLLING_AXIS_MINDELTA)
  1615.             delta = 0;
  1616.         int cumul = ar[1];  // Retrieve cumulative value for easier processing
  1617.         cumul -= ar[on];  // Subtract value in current slot from cumul since it's being thrown away.
  1618.         cumul += delta;  // Add current delta to cumul
  1619.         ar[on] = delta;  // Store the delta at current slot
  1620.         ar[1] = cumul;  // Save cumulative value
  1621.  
  1622.         bOldSig = (BOOL)ar[2];
  1623.         ar[2] = int(bSig = cumul > DEVICE_POLLING_AXIS_SIGNIFICANT);
  1624.         if (bSig)
  1625.         {
  1626.             // This axis is about to be activated.  We now reset the history and cumulative movement since we don't need them any more.
  1627.             ar[0] = DEVICE_POLLING_ACBUF_START_INDEX;
  1628.             ar[1] = 0;
  1629.             ar[2] = FALSE;
  1630.             for (int c = DEVICE_POLLING_ACBUF_START_INDEX;
  1631.                     c < DEVICE_POLLING_ACBUF_START_INDEX + DEVICE_POLLING_AXIS_ACCUMULATION; c++)
  1632.                 ar[c] = 0;
  1633.         }
  1634.     }
  1635.     else
  1636.     {
  1637.         i = m_AxisValueArray.GetSize();
  1638.         m_AxisValueArray.SetSize(i + 1);
  1639.         objid_avai.add(doi.dwType, i);
  1640.         AxisValueArray &ar = m_AxisValueArray[i];
  1641.         ar.SetSize(DEVICE_POLLING_ACBUF_START_INDEX + DEVICE_POLLING_AXIS_ACCUMULATION);
  1642.         ar[0] = DEVICE_POLLING_ACBUF_START_INDEX;
  1643.         ar[1] = 0;
  1644.         ar[2] = FALSE;
  1645.         for (int c = DEVICE_POLLING_ACBUF_START_INDEX;
  1646.                 c < DEVICE_POLLING_ACBUF_START_INDEX + DEVICE_POLLING_AXIS_ACCUMULATION; c++)
  1647.             ar[c] = 0;
  1648.         
  1649.         bOldSig = bSig = FALSE;
  1650.     }
  1651. }
  1652.  
  1653. void CDIDeviceActionConfigPage::AxisDelta(const DIDEVICEOBJECTINSTANCEW &doi, BOOL data, BOOL old)
  1654. {
  1655.     if (data && !old)
  1656.     {
  1657.         if (m_State == CFGSTATE_NORMAL)
  1658.             ActivateObject(doi);
  1659.     }
  1660.     if (old && !data)
  1661.         DeactivateObject(doi);
  1662. }
  1663.  
  1664. void CDIDeviceActionConfigPage::ButtonDelta(const DIDEVICEOBJECTINSTANCEW &doi, DWORD data, DWORD old)
  1665. {
  1666.     static DWORD dwLastOfs;
  1667.     static DWORD dwLastTimeStamp;
  1668.  
  1669.     if (data && !old)
  1670.     {
  1671.         // Do special processing for keyboard
  1672.         if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_KEYBOARD)
  1673.         {
  1674.             // If this is an ENTER key, we enter the assign state if not already in it.
  1675.             if (doi.dwOfs == DIK_RETURN || doi.dwOfs == DIK_NUMPADENTER)
  1676.             {
  1677.                 if (m_State == CFGSTATE_NORMAL && m_pCurControl)
  1678.                     EnterAssignState();
  1679.                 return;  // Do nothing other than entering the assign state.  No highlighting
  1680.             }
  1681.  
  1682.             // DELETE key case
  1683.             // If we are in assign state and there is a control, unassign it.
  1684.             if (doi.dwOfs == DIK_DELETE && m_State == CFGSTATE_ASSIGN && m_pCurControl)
  1685.                 {
  1686.                     UnassignCallout();
  1687.                     return;  // Don't highlight or do pick to click for delete if this press happens during assign state.
  1688.                 }
  1689.  
  1690.             // ESCAPE key case
  1691.             if (doi.dwOfs == DIK_ESCAPE && m_State == CFGSTATE_ASSIGN)
  1692.             {
  1693.                 ExitAssignState();
  1694.                 return;
  1695.             }
  1696.  
  1697.             // For all other keys, still process click-to-pick or highlighting.
  1698.         }
  1699.  
  1700.         // Enter assign state if this is a double activation
  1701.         if (m_State == CFGSTATE_NORMAL)
  1702.         {
  1703.             ActivateObject(doi);
  1704.  
  1705.             if (doi.dwOfs == dwLastOfs && dwLastTimeStamp + GetDoubleClickTime() > GetTickCount())
  1706.             {
  1707.                 // We check if a callout for this control exists.  If not, do not enter assign state.
  1708.                 CDeviceView *pCurView = m_pDeviceUI->GetCurView();
  1709.                 CDeviceControl *pControl = pCurView->GetControlFromOfs(doi.dwType);
  1710.                 if (pControl)
  1711.                     EnterAssignState();
  1712.             }
  1713.             dwLastOfs = doi.dwOfs;
  1714.             dwLastTimeStamp = GetTickCount();
  1715.         }
  1716.     }
  1717.     if (old && !data)
  1718.         DeactivateObject(doi);
  1719. }
  1720.  
  1721. void CDIDeviceActionConfigPage::PovDelta(const DIDEVICEOBJECTINSTANCEW &doi, DWORD data, DWORD old)
  1722. {
  1723.     BOOL d = data != -1, o = old != -1;
  1724.  
  1725.     if (d && !o)
  1726.     {
  1727.         if (m_State == CFGSTATE_NORMAL)
  1728.             ActivateObject(doi);
  1729.     }
  1730.     if (o && !d)
  1731.         DeactivateObject(doi);    
  1732. }
  1733.  
  1734. void CDIDeviceActionConfigPage::ActivateObject(const DIDEVICEOBJECTINSTANCEW &doi)
  1735. {
  1736.     if (m_pDeviceUI == NULL)
  1737.         return;
  1738.  
  1739.  
  1740.     CDeviceView *pCurView = m_pDeviceUI->GetCurView(), *pView = pCurView;
  1741.     if (pView == NULL)
  1742.         return;
  1743.  
  1744.     CDeviceControl *pControl = pView->GetControlFromOfs(doi.dwType);
  1745.     if (pControl == NULL)
  1746.     {
  1747.         for (int i = 0; i < m_pDeviceUI->GetNumViews(); i++)
  1748.         {
  1749.             pView = m_pDeviceUI->GetView(i);
  1750.             if (pView == NULL)
  1751.                 continue;
  1752.  
  1753.             pControl = pView->GetControlFromOfs(doi.dwType);
  1754.             if (pControl != NULL)
  1755.                 break;
  1756.         }
  1757.  
  1758.         if (pControl != NULL && pView != NULL && pView != pCurView)
  1759.         {
  1760.             // switch to view
  1761.             m_pDeviceUI->SetView(pView);
  1762.             SetControlAssignments();
  1763.             SetCurrentControl(NULL);
  1764.         }
  1765.     }
  1766.  
  1767.     if (pControl != NULL)
  1768.         SetCurrentControl(pControl);
  1769.  
  1770.     SetAppropriateDefaultText();
  1771. }
  1772.  
  1773. void CDIDeviceActionConfigPage::DeactivateObject(const DIDEVICEOBJECTINSTANCEW &doi)
  1774. {
  1775.     // Add code that needs to be run when deactivating here.
  1776. }
  1777.  
  1778. HRESULT CDIDeviceActionConfigPage::Unacquire()
  1779. {
  1780.     if (m_lpDID != NULL)
  1781.         m_lpDID->Unacquire();
  1782.  
  1783.     return S_OK;
  1784. }
  1785.  
  1786. HRESULT CDIDeviceActionConfigPage::Reacquire()
  1787. {
  1788.     InitDevice();
  1789.  
  1790.     return S_OK;
  1791. }
  1792.  
  1793. void CDIDeviceActionConfigPage::EnterAssignState()
  1794. {
  1795.     if (!m_puig->InEditMode())
  1796.         return;
  1797.     if (!m_pCurControl || m_pCurControl->IsFixed())
  1798.         return;
  1799.     SetInfoText(IDS_INFOMSG_EDIT_EDITMODEENABLED);
  1800.     m_State = CFGSTATE_ASSIGN;  // Into the assign state.
  1801.     ShowCurrentControlAssignment();  // Show the tree
  1802.     m_Tree.Invalidate();
  1803.     Invalidate();
  1804. }
  1805.  
  1806. void CDIDeviceActionConfigPage::ExitAssignState()
  1807. {
  1808.     m_State = CFGSTATE_NORMAL;  // Out of the assign state.
  1809.     SetCurrentControl(NULL);  // Unselect the control
  1810.     ShowCurrentControlAssignment();  // Show the tree
  1811.     m_Tree.Invalidate();
  1812.     Invalidate();
  1813.     SetAppropriateDefaultText();
  1814. }
  1815.  
  1816. HRESULT CDIDeviceActionConfigPage::SetInfoText(int iCode)
  1817. {
  1818.     // We check for special code -1 here.  This is only called by CConfigWnd, and means that we should
  1819.     // call SetAppropriateDefaultText to display proper text.
  1820.     if (iCode == -1)
  1821.         SetAppropriateDefaultText();
  1822.     else
  1823.         m_InfoBox.SetText(iCode);
  1824.     return S_OK;
  1825. }
  1826.  
  1827. void CDIDeviceActionConfigPage::SetAppropriateDefaultText()
  1828. {
  1829.     if (m_puig->InEditMode())
  1830.     {
  1831.         if (m_State == CFGSTATE_ASSIGN)
  1832.             SetInfoText(IDS_INFOMSG_EDIT_EDITMODEENABLED);
  1833.         else if (m_pCurControl)
  1834.         {
  1835.             if (m_pCurControl->IsFixed())
  1836.                 SetInfoText(IDS_INFOMSG_APPFIXEDSELECT);
  1837.             else
  1838.                 SetInfoText(IDS_INFOMSG_EDIT_CTRLSELECTED);
  1839.         }
  1840.         else
  1841.         {
  1842.             if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_KEYBOARD)
  1843.                 SetInfoText(IDS_INFOMSG_EDIT_KEYBOARD);
  1844.             else
  1845.             if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_MOUSE)
  1846.                 SetInfoText(IDS_INFOMSG_EDIT_MOUSE);
  1847.             else
  1848.                 SetInfoText(IDS_INFOMSG_EDIT_DEVICE);
  1849.         }
  1850.     } else
  1851.         SetInfoText(IDS_INFOMSG_VIEW_DEVICE);
  1852. }
  1853.